/*
 * =====================================================================================
 *
 *       Filename:  fix_close_wait.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2019年01月10日 16时35分24秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include "list.h"

typedef struct {
    unsigned long cw_inode;
    pid_t pid;
    int and;
    struct list_head list;
}cw_inode_t;


int fix_close_wait();
void kill_all_pid(struct list_head *head);
void good_sleep(unsigned int sec);
int get_all_cw_inodes(struct list_head *head);
unsigned long get_8083_cw_inode(const char *line);
int match_all_pid(struct list_head *head);
void match_pid(const char *pid, struct list_head *head);
pid_t _match_pid(const char *pid, const char *line, struct list_head *head);
void get_inode_and(struct list_head *head1, struct list_head *head2);
void free_all_cw_inode(struct list_head *head);
int not_all_digit(const char *pid_str);
    
int main(int argc, char **argv) 
{
    fix_close_wait();
}


int fix_close_wait()
{
    int ret;
    struct list_head cw1_head;
    struct list_head cw2_head;
    init_list_head(&cw1_head);
    init_list_head(&cw2_head);

    /* 1.  */
    ret = get_all_cw_inodes(&cw1_head);
    if(ret < 0) {
        return -1;
    }
    if(list_empty(&cw1_head)) {
        return 0;
    }

    /* 2.  */
    good_sleep(3);

    /* 3.  */
    ret = get_all_cw_inodes(&cw2_head);
    if(ret < 0) {
        free_all_cw_inode(&cw1_head);
        return -1;
    }
    if(list_empty(&cw2_head)) {
        free_all_cw_inode(&cw1_head);
        return 0;
    }

    /* 4. 交集 */
    get_inode_and(&cw1_head, &cw2_head);
    free_all_cw_inode(&cw2_head);
    if(list_empty(&cw1_head)) {
        return 0;
    }

    /* 5. 找到pid,杀掉 */
    match_all_pid(&cw1_head); 
    kill_all_pid(&cw1_head);
    free_all_cw_inode(&cw1_head);
    return ret;
}

void kill_all_pid(struct list_head *head)
{
    struct list_head *pos;
    list_for_each(pos, head) {
        cw_inode_t *entry = list_entry(pos, cw_inode_t, list);
        if(entry->pid) {
            printf("Fake kill\n");
            //kill(entry->pid, 9);
        }
    }
}

void good_sleep(unsigned int sec)
{
    unsigned int left = sec;
    while(1) {
        left = sleep(left);
        if(0 == left) {
            break;
        }
        else {
            if(EINTR == errno) {
                continue;
            }
        }
    }
}

int get_all_cw_inodes(struct list_head *head)
{
    int ret = 0;
    FILE *fp = fopen("/proc/net/tcp", "r");			
    if(fp == NULL) {				
        if(errno != ENOENT) {				
            perror("fopen(/proc/net/tcp)");					
            return -1;					
        }							
    } 
    do {
        char buffer[256] = {0};
        if(fgets(buffer, sizeof(buffer), fp)) {
            unsigned long inode = get_8083_cw_inode(buffer);
            if(inode > 0) {
                cw_inode_t *entry = (cw_inode_t *)calloc(1, sizeof(cw_inode_t));
                if(NULL == entry) {
                    perror("calloc()");
                    free_all_cw_inode(head);
                    ret = -1;
                    goto end;
                }
                entry->pid = 0;
                entry->and = 0;
                entry->cw_inode = inode;
                list_add_tail(&(entry->list), head);
            }
        }
    } 
    while(!feof(fp));
end:
    fclose(fp);
    return ret;
}

unsigned long get_8083_cw_inode(const char *line)
{
    unsigned long rxq, txq, time_len, retr, inode;
    int num, local_port, rem_port, d, state, uid, timer_run, timeout;
    char rem_addr[128], local_addr[128], more[512];

    num = sscanf(line, 
            "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
            &d, local_addr, &local_port, rem_addr, &rem_port, &state,
            &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);

    if(num < 14) {
        fprintf(stderr, "sscanf return not good\n");
        return -1;
    }
#if 0
    if(local_port != 8083) {
        return 0;
    }
#endif

    if(state != 8) {
        return 0;
    }
    printf("%s", line);
    printf("CLOSE_WAIT\n");
    return inode;
}

int match_all_pid(struct list_head *head)
{
    /* 遍历/proc/pid */
    DIR *dirproc = opendir("/proc");
    if(NULL == dirproc) {
        perror("opendir(/proc)");
        return -1;
    }

    struct dirent *direntproc = NULL;
    while(errno = 0, direntproc = readdir(dirproc)) {
        if(direntproc->d_type != DT_DIR ||
                not_all_digit(direntproc->d_name)) {
            continue;
        }
        char pro_name[128] = {0};
        char pro_name_file[128] = {0};
        sprintf(pro_name_file, "/proc/%s/comm", direntproc->d_name);
        FILE *fp = fopen(pro_name_file, "r");
        if(NULL == fp) {
            continue;
        }
        fgets(pro_name, sizeof(pro_name), fp);
        fclose(fp);
        match_pid(direntproc->d_name, head);
#if 1
        //if(strstr(pro_name, "https_proxy")) {
        if(strstr(pro_name, "python")) {
            match_pid(direntproc->d_name, head);
        }
#endif
    }
    return 0;
}

/*
 * /proc/pid/fd中匹配
 */
void match_pid(const char *pid_str, struct list_head *head)
{
    char name[128] = {0};
    sprintf(name, "/proc/%s/fd", pid_str);
    DIR *dirfd = opendir(name);
    if(NULL == dirfd) {
        return;
    }
    struct dirent *direntfd;
    while((direntfd = readdir(dirfd))) {
        if(direntfd->d_type != DT_LNK) {
            continue;
        }
        char fd_link[128] = {0};
        char line[1024] = {0};
        sprintf(fd_link, "%s/%s", name, direntfd->d_name);
        int ret = readlink(fd_link, line, sizeof(line) - 1);
        if(ret < 0) {
            perror("readlink()");
            continue;
        }
        if(_match_pid(pid_str, line, head) > 0) {
            break;
        }
    }
}


pid_t _match_pid(const char *pid, const char *line, struct list_head *head)
{
    struct list_head *pos;
    list_for_each(pos, head) {
        cw_inode_t *entry = list_entry(pos, cw_inode_t, list);
        if(!entry->and) {
            continue;
        }
        if(entry->pid) {
            continue;
        }
        char inode_info[128] = {0};
        sprintf(inode_info, "socket:[%ld", entry->cw_inode);
        if(strstr(line, inode_info)) {
            entry->pid = (pid_t)atoi(pid);
            return entry->pid;
        }
    }
    return 0;
}

/*
 * 链表交集: 暴力法
 */
void get_inode_and(struct list_head *head1, struct list_head *head2)
{
    /* set and flag */
    struct list_head *pos1;
    struct list_head *pos2;
    list_for_each(pos1, head1) {
        cw_inode_t *entry1 = list_entry(pos1, cw_inode_t, list);
        list_for_each(pos2, head2) {
            cw_inode_t *entry2 = list_entry(pos2, cw_inode_t, list);
            if(entry1->cw_inode == entry2->cw_inode) {
                entry1->and = 1;
                break;
            }
        }
    }
    /* clear not and element */ 
    pos1 = head1->next;
    while(pos1 != head1) {
        cw_inode_t *entry1 = list_entry(pos1, cw_inode_t, list);
        pos1 = pos1->next;
        if(entry1->and) {
            continue;
        }
        list_del(&(entry1->list));
        free(entry1);
    }
}

void free_all_cw_inode(struct list_head *head)
{
    struct list_head *pos;
    pos = head->next;
    while(pos != head) {
        cw_inode_t *entry = list_entry(pos, cw_inode_t, list);
        pos = pos->next;
        list_del(&(entry->list));
        free(entry);
    }
}

int not_all_digit(const char *pid_str)
{
    int i;
    for(i = 0; i < strlen(pid_str); i++) {
        if(pid_str[i] < '0' || pid_str[i] > '9') {
            return 1;
        }
    }
    return 0;
}
